sdl3 ttf font system - #1
Conversation
- add SDL3_ttf fallback font discovery and macOS emoji rendering support - align editor measurement, caret geometry, selection, and search with shaped text - restore large-file editor loading/search performance - normalize UI font sizing after SDL3_ttf migration
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5825fb9795
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| _ = c.TTF_SetTextWrapWhitespaceVisible(result, true); | ||
| } | ||
| if (cacheable) { | ||
| if (self.layouts.count() >= 2048) self.clearLayouts(); |
There was a problem hiding this comment.
Avoid evicting layouts while render holds them
When the cache is full, this clears every cached TTF_Text from inside layout() even if the caller is currently holding a cached Layout; for example render() can get a cache hit for the full text and then the CJK/emoji segment path calls layout() again for a segment. If that nested call crosses this threshold, it destroys the outer shaped.text and the subsequent caret/overlay/TTF draw calls use freed memory after enough distinct short strings have been rendered.
Useful? React with 👍 / 👎.
| if (has_emoji or align_cjk) { | ||
| try self.renderVisualTextSegments(options.font, text, options.text, start, scale, options.color, emoji_box, align_cjk); |
There was a problem hiding this comment.
Restrict emoji segmenting to the overlay path
On Linux and Windows, any string containing an emoji enters renderVisualTextSegments(), which skips emoji clusters, but the only replacement drawing path is emojiTexture() and it returns null off macOS. That makes emoji disappear entirely on non-macOS builds instead of falling back to SDL_ttf's normal text draw; gate this branch on the macOS overlay being available or render the full text for other platforms.
Useful? React with 👍 / 👎.
| if (std.mem.lastIndexOf(u8, text[0..safe_start], query)) |idx| { | ||
| return .{ .start = idx, .end = idx + query.len }; |
There was a problem hiding this comment.
Keep previous search non-overlapping
For overlapping matches such as text aaa with query aa, lastIndexOf returns byte 1, while next search, match counting, and replace-all all advance by query.len and only count byte 0. Pressing Previous can therefore select a match that the counter and replace-all logic don't recognize, producing inconsistent search state; keep the previous search on the same non-overlapping iteration as the rest of the editor.
Useful? React with 👍 / 👎.
Guard cached SDL_ttf layouts against eviction while borrowed, keep emoji segmentation scoped to the macOS overlay path, and align editor previous search with the non-overlapping match sequence used by count and replace.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7528028072
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } | ||
|
|
||
| if (isSingleWidthAscii(cell)) { | ||
| if (isSingleWidthAscii(cell) and dvui.currentWindow().text_engine == null) { |
There was a problem hiding this comment.
Keep ASCII terminal run batching with SDL_ttf
In the normal app after this commit, appInit installs the SDL_ttf text engine into window.text_engine, so this condition is false for every printable ASCII terminal cell. That disables the existing asciiRun batching and turns a dense ASCII terminal frame into one renderText/TTF layout per cell instead of per same-style run, which can make high-output terminal sessions sluggish. Keep the batching path for single-width ASCII with the custom engine and only fall back to per-cell rendering where shaping/fallback actually requires it.
Useful? React with 👍 / 👎.
Restore run batching for single-width ASCII terminal cells when SDL_ttf is installed, while keeping non-ASCII and wide cells on the per-cell path.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b8d9604d4b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return anchor.y + @as(f32, @floatFromInt(local_rows)) * line_h; | ||
| } | ||
|
|
||
| return @as(f32, @floatFromInt(estimatedVisualRows(text[0..bounded], wrap_cols))) * line_h; |
There was a problem hiding this comment.
Use shaped layout for remote-editor search scroll
When a search match is before any trusted byte_heights anchor, this falls back to counting estimated mono columns for the whole prefix. In remote editor files containing combining marks, emoji, RTL text, or other SDL3_ttf-shaped runs, those estimates can diverge from the actual wrap positions, so applyPendingSearchScroll can scroll to the wrong vertical location for offscreen matches even though the new font backend has the real caret/layout geometry available.
Useful? React with 👍 / 👎.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ab1350fb18
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (!activeMatchValid(state, text, query)) { | ||
| state.search_active_index = 0; | ||
| state.search_has_match = false; | ||
| state.search_match_count = 0; | ||
| resetSearchStatsScan(state); |
There was a problem hiding this comment.
Keep match counts valid before selection
When a non-empty query has no active match (for example immediately after the user types in the search box, because clearSearchMatch() just ran), this branch resets the count to 0 and returns before starting the incremental scan. As a result, a file that already contains the query displays 0/0 until the user presses Find, which is a regression from the previous updateSearchStats() path that counted matches independently of the active selection.
Useful? React with 👍 / 👎.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2bea991e30
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| var dirty = state.dirty; | ||
| if (te.text_changed) { | ||
| if (!reloaded_snapshot) dirty = true; | ||
| markSearchStatsDirty(state); |
There was a problem hiding this comment.
Recompute editor dirty state from the current buffer
When a user edits a ready file and then reverts the text back to exactly snapshot.content, this keeps dirty stuck from the previous frame because the new logic starts from state.dirty and only ever sets it to true on te.text_changed. The old comparison against snapshot.content cleared dirty in that scenario, so now closing the editor can keep prompting for unsaved changes even when the buffer matches the remote snapshot; compute dirty from current_text versus snapshot.content after processing events.
Useful? React with 👍 / 👎.
| else | ||
| (if (rtl) substring.rect.x else substring.rect.x + substring.rect.w)); | ||
| return .{ | ||
| .x = (x_px + emojiCompensationBefore(layout_text, text, byte_offset, emoji_box)) / scale, |
There was a problem hiding this comment.
Limit emoji caret compensation to the current visual line
On macOS emoji-overlay text, caretPoint adds compensation for every emoji before byte_offset in the entire buffer, but TTF_GetTextSubString returns an x coordinate relative to the caret's visual line. If an earlier line contains emoji, the caret x for later explicit or wrapped lines is shifted by that previous-line emoji delta, so multiline TextEntry/editor cursor placement and scroll-to-cursor geometry become horizontally wrong; only emoji on the same visual line before the caret should affect this x value.
Useful? React with 👍 / 👎.
No description provided.